//==========================================================================
// Copyright (c) 2000-2009,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "CGrafixLayer.h"
#include "_CGrafixLayer.cpp"

CGrafixLayer::CGrafixLayer(
    /* [in] */ CGrafixEngine * pEngine,
    /* [in] */ Handle32 layer)
{
    pEngine->AddRef();
    m_engine = pEngine;

    m_enable = FALSE;
    m_vga = NULL;
    m_info.layer = layer;
}

ECode CGrafixLayer::SetOpacity(
    /* [in] */ Int32 opacity)
{
    ECode   ec = NOERROR;

    g_lock.Lock();

    if (m_enable) {
        MemoryBuf_<1> bArray;
        bArray.SetUsed(1);
        bArray[0] = opacity;
        ec = m_vga->Control(14, bArray, NULL, NULL);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
    }
    else {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The layer doesn't be enable\n"));

        ec = E_LAYER_NOT_ENABLE;
    }

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixLayer::Render(
    /* [in] */ CGrafixSurface * pSurface)
{
    assert(pSurface && pSurface->m_cacheBuffer);
    return RenderRegion(RECTANGLE_VALS(&pSurface->m_bornRectangle), pSurface);
}

ECode CGrafixLayer::Render()
{
    ECode ec = NOERROR;

    g_lock.Lock();

    if (m_enable) {
        ec = m_vga->Control(11, NULL_MEMORYBUF, NULL, NULL);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
    }
    else {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The layer doesn't be enable\n"));

        ec = E_LAYER_NOT_ENABLE;
    }

Exit:
    g_lock.Unlock();

    return ec;
}

ECode CGrafixLayer::RenderRegion(
        /* [in] */ Int32 x,
        /* [in] */ Int32 y,
        /* [in] */ Int32 width,
        /* [in] */ Int32 height)
{
    return RenderRegion(
        x, y, width, height, m_engine->m_stack->m_primarySurface);
}

ECode CGrafixLayer::RenderRegion(
    /* [in] */ Int32 x,
    /* [in] */ Int32 y,
    /* [in] */ Int32 width,
    /* [in] */ Int32 height,
    /* [in] */ CGrafixSurface * pSurface)
{
    ECode ec = NOERROR;

    assert(pSurface && pSurface->m_cacheBuffer);

    g_lock.Lock();

    if (m_enable) {
        MemoryBuf_<sizeof(RenderFrame)> buffer;
        RenderFrame* frame = (RenderFrame*)buffer.GetPayload();
        BitmapBufInfo* info = &pSurface->m_cacheBuffer->m_buffer.info;

        if (width > m_engine->m_stack->m_width) {
            width = m_engine->m_stack->m_width;
        }

        if (height > m_engine->m_stack->m_height) {
            height = m_engine->m_stack->m_height;
        }

        frame->startX = x;
        frame->startY = y;
        frame->srcBuffer.width = width;
        frame->srcBuffer.height = height;
        frame->srcBuffer.pitch = info->pitch;
        frame->srcBuffer.address = info->address;
        frame->srcBuffer.bitsPerPixel = info->bitsPerPixel;

        buffer.SetUsed(sizeof(RenderFrame));

        ec = m_vga->Control(11, buffer, NULL, NULL);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }
    }
    else {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The layer doesn't be enable\n"));

        ec = E_LAYER_NOT_ENABLE;
    }

Exit:
    g_lock.Unlock();

    return ec;
}

//
// TODO: optimize
//
Void CGrafixLayer::Render2Framebuffer(
    /* [in] */ CGrafixSurface * pSurface,
    /* [in] */ Rectangle * pUpdateRectangle,
    /* [in] */ ScreenOrientation orientation)
{
    assert(pSurface);
    assert(0 == pSurface->m_bornRectangle.x && 0 == pSurface->m_bornRectangle.y);
    assert(pSurface->m_bornRectangle.width == m_info.screen.width
        || pSurface->m_bornRectangle.height == m_info.screen.width);
    assert(pSurface->m_bornRectangle.height == m_info.screen.height
        || pSurface->m_bornRectangle.width == m_info.screen.height);
    assert(PixelFormat_ARGB == pSurface->m_format
        || PixelFormat_RGB24 == pSurface->m_format);

    assert(pUpdateRectangle && RectangleValid(pUpdateRectangle));

    g_lock.Lock();

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The layer doesn't be enable\n"));

        goto Exit;
    }

    if (!RectangleRegionIntersect(pUpdateRectangle, &pSurface->m_clipRegion)) {
        goto Exit;
    }

    if (PixelFormat_RGBT666 == m_info.pixelFormat) {
        assert(1 == m_info.addressCount);

        RGB24ToRGB666(
                    (Address)m_info.systemAddress[0],
                        m_info.screen.width,
                        m_info.screen.height,
                        m_info.screen.width<<2,
                    (Address)pSurface->m_cacheBuffer->m_buffer.info.address,
                        pSurface->m_cacheBuffer->m_buffer.info.pitch,
                    pUpdateRectangle->x,
                        pUpdateRectangle->y,
                        pUpdateRectangle->width,
                        pUpdateRectangle->height,
                    orientation);
    }
    else if (PixelFormat_RGB565 == m_info.pixelFormat) {
        assert(1 == m_info.addressCount);

        RGB24ToRGB565(
                    (Address)m_info.systemAddress[0],
                        m_info.screen.width,
                        m_info.screen.height,
                        m_info.screen.width<<1,
                    (Address)pSurface->m_cacheBuffer->m_buffer.info.address,
                        pSurface->m_cacheBuffer->m_buffer.info.pitch,
                    pUpdateRectangle->x,
                        pUpdateRectangle->y,
                        pUpdateRectangle->width,
                        pUpdateRectangle->height,
                    orientation);
    }
#ifdef _WIN32
    else if (PixelFormat_RGB24 == m_info.pixelFormat) {
        assert(1 == m_info.addressCount);

        RGB24ToRGB888(
                    (Address)m_info.systemAddress[0],
                        m_info.screen.width,
                        m_info.screen.height,
                        m_info.screen.width<<1,
                    (Address)pSurface->m_cacheBuffer->m_buffer.info.address,
                        pSurface->m_cacheBuffer->m_buffer.info.pitch,
                    pUpdateRectangle->x,
                        pUpdateRectangle->y,
                        pUpdateRectangle->width,
                        pUpdateRectangle->height,
                    orientation);
    }
#endif
    else {
        switch(orientation) {
        case ScreenOrientation_Up: {
            BitmapBufInfo* srcBuffer = &pSurface->m_cacheBuffer->m_buffer.info;

            UInt32 srcPitch = srcBuffer->pitch;
            UInt32 srcAddr = srcBuffer->address
                        + srcPitch * pUpdateRectangle->y
                        + (srcBuffer->bitsPerPixel * pUpdateRectangle->x >> 3);

            UInt32 dstPitch = (m_info.screen.width * m_info.bitsPerPixel) >> 3;
            UInt32 dstAddr = m_info.systemAddress[0]
                        + dstPitch * pUpdateRectangle->y
                        + (m_info.bitsPerPixel * pUpdateRectangle->x >> 3);

            UInt32 width = pUpdateRectangle->width;
            UInt32 height = pUpdateRectangle->height;

            switch(m_info.pixelFormat) {
            case PixelFormat_RGBT555: {
                assert(1 == m_info.addressCount);

                for (UInt32 h = 0; h < height; h++) {
                    UInt16* dst = (UInt16 *)dstAddr;
                    UInt32* src = (UInt32 *)srcAddr;
                    for (UInt32 w = 0; w < width; w++) {
                        *dst++ = RGB24Format2RGBT555(*src++);
                    }

                    dstAddr += dstPitch;
                    srcAddr += srcPitch;
                }

                break;
            }

            case PixelFormat_ARGB: {
                assert(1 == m_info.addressCount);

                for (UInt32 h = 0; h < height; h++) {
                    UInt32* dst = (UInt32 *)dstAddr;
                    UInt32* src = (UInt32 *)srcAddr;
                    for (UInt32 w = 0; w < width; w++) {
                        *dst++ = *src++;
                    }

                    dstAddr += dstPitch;
                    srcAddr += srcPitch;
                }

                break;
            }

            default:
                GFX_ERROR_MESSAGE("The pixelformat doesn't support, pls check\n");
                assert(0);
            }

            break;
        }


        default: {
            Region updateRegion = REGION_INIT_FROM_RECTANGLE(pUpdateRectangle);
            Int32 x, y;

            switch(m_info.pixelFormat) {
            case PixelFormat_RGBT555: {
                assert(1 == m_info.addressCount);

                UInt32* srcAddr =
                        (UInt32 *)pSurface->m_cacheBuffer->m_buffer.info.address;
                UInt16* dstAddr = (UInt16 *)m_info.systemAddress[0];

                for (y = updateRegion.top; y < updateRegion.bottom; y++) {
                    for (x = updateRegion.left; x < updateRegion.right; x++) {
                        Int32 Position = x * g_RenderMatrix[orientation].xx
                                + y * g_RenderMatrix[orientation].yx
                                + g_RenderMatrix[orientation].x0
                                    + (x * g_RenderMatrix[orientation].xy
                                    + y * g_RenderMatrix[orientation].yy
                                    + g_RenderMatrix[orientation].y0)
                                        * m_info.screen.width;
                        assert (Position >= 0);
                        dstAddr[Position] = RGB24Format2RGBT555(
                                srcAddr[x + y * pSurface->m_bornRectangle.width]);

                    }
                }

                break;
            }

            default:
                GFX_ERROR_MESSAGE("The pixelformat doesn't support, pls check\n");
                assert(0);
            }
        }
        }
    }
Exit:
    g_lock.Unlock();
}

Void CGrafixLayer::TranparentLayer(
        /* [in] */ Rectangle * pTransparentRectangle,
        /* [in] */ ScreenOrientation orientation)
{
    if (PixelFormat_RGB565 == m_info.pixelFormat) {
        return;
    }

    g_lock.Lock();

    Address dstAddr;
    UInt32 bpp = m_info.bitsPerPixel;
    UInt32 dstPitch = m_info.screen.width * bpp >> 3;
    Int32 x;
    Int32 y;

    if (!m_enable) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("The layer doesn't be enable\n"));

        goto Exit;
    }

    x = pTransparentRectangle->x * g_RenderMatrix[orientation].xx
        + pTransparentRectangle->y * g_RenderMatrix[orientation].yx
        + g_RenderMatrix[orientation].x0;

    y = pTransparentRectangle->x * g_RenderMatrix[orientation].xy
        + pTransparentRectangle->y * g_RenderMatrix[orientation].yy
        + g_RenderMatrix[orientation].y0;

    if (orientation % 2) {
        Swap(pTransparentRectangle->width, pTransparentRectangle->height);
    }

    if (!RectangleIntersect(pTransparentRectangle, &m_info.screen)) {
        goto Exit;
    }

    switch (orientation) {
    case ScreenOrientation_Left:
        x -= pTransparentRectangle->width - 1;

        break;

    case ScreenOrientation_Down:
        x -= pTransparentRectangle->width - 1;
        y -= pTransparentRectangle->height - 1;

        break;

    case ScreenOrientation_Right:
        y -= pTransparentRectangle->height - 1;
        break;
    }

    pTransparentRectangle->x = x;
    pTransparentRectangle->y = y;

    switch(m_info.pixelFormat) {
    case PixelFormat_RGBT555: {
        dstAddr = m_info.systemAddress[0]
            + pTransparentRectangle->y * dstPitch
            + (pTransparentRectangle->x * bpp >> 3);

        for (Int32 i = 0; i < pTransparentRectangle->height; i++) {
            UInt16* p = (UInt16 *)dstAddr;
            for (Int32 j = 0; j < pTransparentRectangle->width; j++) {
                *p++ |= 0x8000;
            }

            dstAddr += dstPitch;
        }

        break;
    }

    case PixelFormat_RGBT666: {
        dstAddr = m_info.systemAddress[0]
            + pTransparentRectangle->y * dstPitch
            + (pTransparentRectangle->x * bpp >> 3);

        for (Int32 i = 0; i < pTransparentRectangle->height; i++) {
            UInt32* p = (UInt32 *)dstAddr;
            for (Int32 j = 0; j < pTransparentRectangle->width; j++) {
                *p++ |= 0x40000;
            }

            dstAddr += dstPitch;
        }

        break;
    }

    case PixelFormat_RGB24: {
        dstAddr = m_info.systemAddress[0]
            + pTransparentRectangle->y * dstPitch
            + (pTransparentRectangle->x * bpp >> 3);

        for (Int32 i = 0; i < pTransparentRectangle->height; i++) {
            UInt32* p = (UInt32 *)dstAddr;
            for (Int32 j = 0; j < pTransparentRectangle->width; j++) {
                *p++ &= 0x00FFFFFF;
            }

            dstAddr += dstPitch;
        }

        break;
    }
    }

Exit:
    g_lock.Unlock();
}

ECode CGrafixLayer::GetAttributes(
    /* [out] */ struct GrafixLayerFormat * pLayerFormat,
    /* [out] */ Boolean * pEnabled)
{
    if (!pLayerFormat || !pEnabled) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("Invalid argument\n"));

        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;

    g_lock.Lock();

    *pEnabled = m_enable;
    if (m_enable) {
        pLayerFormat->pixelFormat = m_info.pixelFormat;
        pLayerFormat->layerArea = m_info.screen;
        pLayerFormat->grafixLayerIndex = m_info.layer;
        pLayerFormat->count = m_info.addressCount;

        for(Int32 i = 0; i < m_info.addressCount; i++) {
#if defined(_zener)
            pLayerFormat->address[i] = m_info.systemAddress[i];
#elif defined(_win32)
            ec = ConvertAllocAddrToId(
                            m_info.systemAddress[i],
                            0,
                            (unsigned long *)&pLayerFormat->address[i]);
            if (GFX_FAILED(ec)) {
                goto Exit;
            }
#elif defined(_wince)
            pLayerFormat->address[i] = m_info.systemAddress[i];
#else
# add the code of the new platform
#endif
            pLayerFormat->size[i] = m_info.size[i];
        }
    }

#if defined(_win32)
Exit:
#endif

    g_lock.Unlock();

    return ec;
}
